#include <iostream>
#include <vector>
#include <map>
#include <fstream>
#include <cstdlib>
#include <sstream>

//---------------------------------------------------------------------------
//// RDF-3X
//// (c) 2008 Thomas Neumann. Web site: http://www.mpi-inf.mpg.de/~neumann/rdf3x
////
//// This work is licensed under the Creative Commons
//// Attribution-Noncommercial-Share Alike 3.0 Unported License. To view a copy
//// of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/
//// or send a letter to Creative Commons, 171 Second Street, Suite 300,
//// San Francisco, California, 94105, USA.
////---------------------------------------------------------------------------
//
using namespace std;

bool smallAddressSpace()
	// Is the address space too small?
{
	return sizeof(void*)<8;
}

class MyVertex {
	public:
		MyVertex(string s) {
			name = s;
			belongsToPartition= -1;
		}

		void setPartitionId(int a) {
			belongsToPartition = a;
		}

		string getName() {
			return name;
		}

		int belongsToPartition;
		string name;

};


class Edge {
	public:
		long int destVertexId;
		long int edgeId;

		Edge();
		Edge(long int destId, long int id) {
			destVertexId = destId;
			edgeId = id;
		}

};

// Creates a graph and int->string maps from the given set of triples
bool parseAndCreateGraph(istream &in, map <string, long int> &subObjMap, map <string, long int> &predMap, 
		map <long int, vector <Edge> > &graph,  map <long int, MyVertex> &revSubObjMap, map <long int, string> &revPredMap) {

	string subject, predicate, object;
	long int subObjId = 0, predId = 0;
	typedef vector <Edge> QueueDef;
	typedef map <long int, QueueDef> graphStd;
	while(!in.eof()) {
		getline(in, subject, ' ');
		if(subject == "")
			break;
		getline(in, predicate, ' ');
		if(predicate == "")
			return false;
		getline(in, object);
		if(object == "")
			return false;
	//	cout << subject << " " << predicate << " " << object << endl;
		
		if(subObjMap.find(subject) == subObjMap.end()) {
			revSubObjMap.insert(map <long int, MyVertex>::value_type(subObjId, MyVertex(subject)));
			subObjMap[subject] = subObjId++;
		}
		
		if(subObjMap.find(object) == subObjMap.end()) {
			revSubObjMap.insert(map <long int, MyVertex>::value_type(subObjId, MyVertex(object)));
			subObjMap[object] = subObjId++;
		}

		if(predMap.find(predicate) == predMap.end()) {
			revPredMap[predId] = predicate;	
			predMap[predicate] = predId++;
		}
		
		int subId = subObjMap[subject], objId = subObjMap[object], predId = predMap[predicate];
		//If graph doesnt have this vertex add the vertex with empty vector
		if(graph.find(subId) == graph.end()) {
			vector <Edge> q;
			q.push_back(Edge(objId, predId));
			graph.insert(graphStd::value_type(subId, q));

		} else {
			graph[subId].push_back(Edge(objId, predId));
		}

	}
	return true;
}

void addTripleToFile(string sub, string obj, string pred, ofstream &file) {
	file << sub << " " << pred << " " << obj << "\n";
}

void print( map <long int, vector <Edge> > graph) {
	int size = graph.size();
	for(int i=0; i<size; i++) {
		cout << i << "-> ";
		int size2 = graph[i].size();
		for (int j=0; j< size2; j++) {
			cout << graph[i][j].edgeId << "," << graph[i][j].destVertexId << " ";
		}
		cout << endl;
	}

}

void createPartitions( map <string, long int> &subObjMap, map <long int, vector <Edge> > &graph,  map <long int, MyVertex> &revSubObjMap, 
		map <long int, string> &revPredMap, int numPartitions, char *file) {
	long int vertexCount = subObjMap.size(), vertexID=0;
	int partID = 1;
	//print(graph);
	while(partID <= numPartitions) {
		//create file and add one level
		ostringstream s;
		// creating temp file name
		s << file << partID;
		ofstream tmpfile;
		tmpfile.open(s.str().c_str());
		
		while(vertexID < (vertexCount - ((numPartitions - partID) * vertexCount)/numPartitions)) {
			if( revSubObjMap.find(vertexID)->second.belongsToPartition != partID) {
//				cout << "partID = " << partID << " vertexID = " << vertexID << "\n";

				revSubObjMap.find(vertexID)->second.belongsToPartition = partID;
				int edgeCount = graph[vertexID].size();
				for(int i=0; i<edgeCount; i++) {
					long int destVertexId = graph[vertexID][i].destVertexId;
					long int edgeId = graph[vertexID][i].edgeId;
					addTripleToFile(revSubObjMap.find(vertexID)->second.name, revSubObjMap.find(destVertexId)->second.name, 
							revPredMap[edgeId], tmpfile);

					// 2-hop guarranty
					if(revSubObjMap.find(destVertexId)->second.belongsToPartition != partID) {

						revSubObjMap.find(destVertexId)->second.belongsToPartition = partID;
						int destVertexEdgeCount = graph[destVertexId].size();
						for(int i=0; i<destVertexEdgeCount; i++) {
							addTripleToFile(revSubObjMap.find(destVertexId)->second.name, 
									revSubObjMap.find(graph[destVertexId][i].destVertexId)->second.name, 
									revPredMap[graph[destVertexId][i].edgeId], tmpfile);
						}
					}
				}
			}
			vertexID++;
		}
		partID++;
		tmpfile.close();
	}
}

int main(int argc,char* argv[])
{
	if (smallAddressSpace())
		cerr << "Warning: Running RDF-3X on a 32 bit system is not supported and will fail for large data sets. Please use a 64 bit system instead!" << endl;

	cerr << "RDF-3X turtle importer" << endl
		<< "(c) 2008 Thomas Neumann. Web site: http://www.mpi-inf.mpg.de/~neumann/rdf3x" << endl;

	if (argc<2) {
		cerr <<  "usage: " << argv[0] << " [input] numb_partitions" << endl
			<< "without input file data is read from stdin" << endl;
		return 1;
	}

	ifstream in;
	in.open(argv[1]);
	if (!in.is_open()) {
		cerr << "Unable to open " << argv[1] << endl;
		return 1;
	}


	map <string, long int> subObjMap, predMap;
	map <long int, MyVertex> revSubObjMap;
        map <long int, string>	revPredMap;
	map <long int, vector<Edge> > graph;

	// Parses the given input triples file into given number of partitions
	// and creates an Graph
	if(!parseAndCreateGraph (in, subObjMap, predMap, graph, revSubObjMap, revPredMap))
		return 1;
	in.close();

	createPartitions(subObjMap, graph, revSubObjMap, revPredMap, atoi(argv[2]), argv[1]);

	return 0;
}

